package com.echi.watermarker;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;


public class PdfWaterMarkerUtil {
    private static final Logger LOGGER = LogManager.getLogger(PdfWaterMarkerUtil.class);

    /**
     * pdf添加单个水印，居中
     *
     * @param inputFile  需要添加水印的文件
     * @param outputFile 添加完水印的文件存放路径
     * @param waterMark  需要添加的水印文字
     * @param opacity    水印字体透明度
     * @param fontsize   水印字体大小
     * @param angle      水印倾斜角度（0-360）
     */
    public static void addWaterMark(String inputFile, String outputFile, String waterMark,
                                    float opacity, int fontsize, int angle) {
        processFile(inputFile, outputFile, waterMark, opacity, fontsize, angle, 0, false);
    }


    /**
     * pdf添加多个水印，平铺
     *
     * @param inputFile         需要添加水印的文件
     * @param outputFile        添加完水印的文件存放路径
     * @param waterMark         需要添加的水印文字
     * @param opacity           水印字体透明度
     * @param fontsize          水印字体大小
     * @param angle             水印倾斜角度（0-360）
     * @param waterMarkInterval 水印间隔，由于是上下的间隔，所以这个数字最好大于100，否则会导致水印重叠
     */
    public static void addWaterMark(String inputFile, String outputFile, String waterMark,
                                    float opacity, int fontsize, int angle, int waterMarkInterval) {
        processFile(inputFile, outputFile, waterMark, opacity, fontsize, angle, waterMarkInterval, true);
    }

    /**
     * pdf reader添加水印的处理方法，支持单个水印，居中
     *
     * @param reader    PdfReader
     * @param stamper   PdfStamper
     * @param waterMark 水印文字
     * @param opacity   水印透明度
     * @param fontsize  水印字体大小
     * @param angle     水印倾斜角度
     * @throws DocumentException
     **/
    public static void addWaterMarkOnPdfReader(PdfReader reader, PdfStamper stamper, String waterMark, float opacity, int fontsize, int angle) throws DocumentException, IOException {
        processPdfReader(reader, stamper, waterMark, opacity, fontsize, angle, 0, false);
    }

    /**
     * pdf reader添加水印的处理方法，支持多个水印，平铺
     *
     * @param reader            PdfReader
     * @param stamper           PdfStamper
     * @param waterMark         水印文字
     * @param opacity           水印透明度
     * @param fontsize          水印字体大小
     * @param angle             水印倾斜角度
     * @param waterMarkInterval 水印间隔，由于是上下的间隔，所以这个数字最好大于100，否则会导致水印重叠
     * @param multi             是否多水印
     * @throws DocumentException
     **/
    public static void addWaterMarkOnPdfReader(PdfReader reader, PdfStamper stamper, String waterMark, float opacity, int fontsize, int angle, int waterMarkInterval, boolean multi) throws DocumentException, IOException {
        processPdfReader(reader, stamper, waterMark, opacity, fontsize, angle, waterMarkInterval, multi);
    }


    /**
     * pdf添加水印的处理方法，支持多个水印，平铺
     *
     * @param inputFile         需要添加水印的文件
     * @param outputFile        添加完水印的文件存放路径
     * @param waterMark         需要添加的水印文字
     * @param opacity           水印字体透明度
     * @param fontsize          水印字体大小
     * @param angle             水印倾斜角度（0-360）
     * @param waterMarkInterval 水印间隔
     * @param multi             是否多水印
     */
    private static void processFile(String inputFile, String outputFile, String waterMark, float opacity, int fontsize, int angle, int waterMarkInterval, boolean multi) {
        File file = new File(inputFile);
        if (!file.exists()) {
            return;
        }

        File outFile = new File(outputFile);
        if (file.exists()) {
            outFile.deleteOnExit();
        }
        PdfReader reader = null;
        PdfStamper stamper = null;
        try {
            reader = new PdfReader(inputFile);
            stamper = new PdfStamper(reader, Files.newOutputStream(Paths.get(outputFile)));
            processPdfReader(reader, stamper, waterMark, opacity, fontsize, angle, waterMarkInterval, multi);
        } catch (IOException | DocumentException e) {
            LOGGER.error("添加水印失败：", e);
        } finally {
            //关闭流
            if (stamper != null) {
                try {
                    stamper.close();
                } catch (DocumentException | IOException e) {
                    LOGGER.error("添加水印失败：", e);
                }
            }
            if (reader != null) {
                reader.close();
            }
        }
    }


    /**
     * 使用pdf reader的方式，处理pdf水印，一般用于在处理pdf的过程中直接加水印
     *
     * @param reader            PdfReader
     * @param stamper           PdfStamper
     * @param waterMark         水印文字
     * @param opacity           水印透明度
     * @param fontsize          水印字体大小
     * @param angle             水印倾斜角度
     * @param multi             是否多水印
     * @param waterMarkInterval 水印间隔，由于是上下的间隔，所以这个数字最好大于100，否则会导致水印重叠
     * @param multi             是否多水印
     * @throws DocumentException
     **/
    private static void processPdfReader(PdfReader reader, PdfStamper stamper, String waterMark, float opacity, int fontsize, int angle, int waterMarkInterval, boolean multi) throws DocumentException, IOException {

        BaseFont baseFont;
        //如果服务器没有上面那个字体，再使用自定义字体，配置里定义
        if (PdfWaterMarkConfig.useFont) {
            baseFont = loadFont();
            if (baseFont == null) {
                LOGGER.error("无法加载字体");
                return;
            }
        } else {
            baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
        }
        PdfGState gs = new PdfGState();
        //这里是透明度设置
        gs.setFillOpacity(opacity);
        //这里是条纹不透明度
        gs.setStrokeOpacity(0.2f);
        int total = reader.getNumberOfPages() + 1;
        JLabel label = new JLabel();
        label.setText(waterMark);
        PdfContentByte under;
        Rectangle pageRect;
        //这个循环是确保每一张PDF都加上水印
        for (int i = 1; i < total; i++) {
            pageRect = reader.getPageSizeWithRotation(i);
            under = stamper.getOverContent(i);  //在内容上方添加水印
            under.saveState();
            under.setGState(gs);
            under.beginText();
            //under.setColorFill(BaseColor.PINK);  //添加文字颜色  不能动态改变 放弃使用
            under.setFontAndSize(baseFont, fontsize); //这里是水印字体大小
            //是否多水印
            if (multi) {
                FontMetrics metrics = label.getFontMetrics(label.getFont());
                //字符串的高,   只和字体有关
                int waterMarkHeight = metrics.getHeight();
                //字符串的宽
                int watermarkWeight = metrics.stringWidth(label.getText());
                // 水印间隔距离
                waterMarkInterval = waterMarkInterval == 0 ? 150 : waterMarkInterval;
                // 在高度和宽度维度每隔waterMarkInterval距离添加一个水印
                for (int height = waterMarkHeight; height < pageRect.getHeight(); height = height + waterMarkInterval) {
                    for (int width = watermarkWeight; width < pageRect.getWidth() + watermarkWeight;
                         width = width + waterMarkInterval) {
                        // 添加水印文字并旋转指定角度度角
                        under.showTextAligned(Element.ALIGN_LEFT, waterMark, width - (watermarkWeight - fontsize),
                                height - waterMarkHeight, angle);
                    }
                }
            } else {
                //单个水印，默认居中
                under.showTextAligned(Element.ALIGN_CENTER, waterMark, pageRect.getWidth() / 2, pageRect.getHeight() / 2, angle);
            }
            //添加水印文字
            under.endText();
        }
    }


    private static BaseFont loadFont() {
        // 使用类加载器获取字体文件路径
        ClassLoader classLoader = PdfWaterMarkerUtil.class.getClassLoader();
        String fontPath = classLoader.getResource("fonts/SimSun.ttf").getPath();
        try {
            return BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        } catch (DocumentException | IOException e) {
            LOGGER.error("加载字体失败：", e);
            return null;
        }
    }
}
